#include "SehnsuchtPrecompiled.hxx"
#include "SehnsuchtLoader.hxx"
#include "SehnsuchtDDSTextureLoader.hxx"

using namespace Sehnsucht::IO;
using namespace Sehnsucht;

typedef std::unique_ptr<Char16[]> UniqueChar16Ptr;

#ifdef REIGN_DESKTOP
#define EmptyString L""
#define GetStringData( pStr ) pStr.data()
#define GetStringLength( pStr ) pStr.size()
#define StringAt( pStr, iIndex ) pStr[iIndex]
#define ToLowerInvariant towlower
#define DDSExtension (L"dds") 
#define DotChar (L'.')
#define IsDDSExtension( pStr ) (pStr == DDSExtension)
//#define IsDDSExtension( pStr ) ((lstrcmp( GetExtension(pStr).data(), DDSExtension)) == 0)
#else
#define EmptyString ""
#define GetStringData( pStr ) pStr->Data()
#define GetStringLength( pStr ) pStr->Length()
#define StringAt( pStr, iIndex ) pStr[iIndex]
#define ToLowerInvariant tolower
#define DDSExtension ("dds")
#define DotChar ('.')
#define IsDDSExtension( pStr ) (pStr == DDSExtension)
#endif

#ifndef REIGN_PHONE
BasicLoader::BasicLoader(
	_In_ BasicLoader::Device3Ptr pDevice,
	_In_ BasicLoader::WicFactory2Ptr pFactory
	) : m_d3dDevice(pDevice), m_wicFactory(pFactory)
#else
BasicLoader::BasicLoader(
	_In_ BasicLoader::Device3Ptr pDevice
	) : m_d3dDevice(pDevice)
#endif
{
#ifdef REIGN_DESKTOP
	m_Reader.reset();
	m_Reader = UniquePtrReader( alloc_new Reader() );
#else
	m_Reader = alloc_ref_new Reader();
#endif
}

BasicLoader::StringArg BasicLoader::GetExtension( 
	_In_ BasicLoader::StringArg wszFileName 
	)
{
	auto iLastDot = (-1);
	auto iLength = _To_uint GetStringLength(wszFileName);

	for ( auto i = iLength - 1; i >= 0 && iLastDot == (-1); i-- )
	{
		if ( *(GetStringData(wszFileName) + i) == DotChar )
		{
			iLastDot = i;
			break;
		}
	}

	if ( iLastDot != (-1) )
	{
		auto iExtensionLength = iLength - iLastDot;
        UniqueChar16Ptr wszExtension( alloc_new Char16[iExtensionLength] );

		for ( auto i = 0u; i < iExtensionLength; i++ )
		{
			auto iOffset = iLastDot + 1 + i; // Get the offset 
			StringAt(wszExtension, i) = ToLowerInvariant( // Fill out the extension string
				*(GetStringData(wszFileName) + iOffset) 
				);
		}

#ifndef REIGN_DESKTOP
		return alloc_ref_new Platform::String(wszExtension.get());
#else
		return String16(wszExtension.get());

#endif
	}

	return EmptyString;
}

Void BasicLoader::CreateTexture(
	_In_ Bool bDecodeAsDDS,
    _In_reads_bytes_(uDataSize) BytePtr pData,
    _In_ UInt32 uDataSize,
    _Out_opt_ Texture2Address ppTexture,
    _Out_opt_ ShaderResourceViewAddress ppView,
    _In_opt_ BasicLoader::StringArg wszName
	)
{
	Microsoft::WRL::ComPtr<ShaderResourceView> tempView;
	Microsoft::WRL::ComPtr<Texture2> tempTexture;

	if ( !bDecodeAsDDS )
	{
		Microsoft::WRL::ComPtr<Resource> tempTextureResource;

		if ( ppView == nullptr )
		{
			Sehnsucht::CreateDDSTextureFromMemory(
				m_d3dDevice,
				pData,
				uDataSize,
				tempTextureResource.GetAddressOf(),
				nullptr
				);
		}
		else
		{
			Sehnsucht::CreateDDSTextureFromMemory(
				m_d3dDevice,
				pData,
				uDataSize,
				tempTextureResource.GetAddressOf(),
				tempView.GetAddressOf()
				);
		}

		Sehnsucht::ThrowIfFailed(
			tempTextureResource.As(&tempTexture)
			);

		tempTextureResource = nullptr;
	}
	else
	{
		// WIC is not available for Phone (all the D2D actually). 
		// TODO: #ifndef "Phone" => Implement for Metro and Desktop. #endif

#ifdef REIGN_PHONE
		Sehnsucht::ThrowIfFailed(
			E_NOTIMPL
			);
#else

		if (m_wicFactory.Get() == nullptr)
        {
            // A WIC factory object is required in order to load texture assets stored in non-DDS formats.  
			// If BasicLoader was not initialized with one, create one as needed.

            Sehnsucht::ThrowIfFailed(
                CoCreateInstance(
                    CLSID_WICImagingFactory, nullptr,
                    CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_wicFactory)
                    )
                );
        }

        Microsoft::WRL::ComPtr<IWICStream> wicStream;
        Sehnsucht::ThrowIfFailed(
            m_wicFactory->CreateStream(&wicStream)
            );

        Sehnsucht::ThrowIfFailed(
            wicStream->InitializeFromMemory(
                pData, uDataSize
                )
            );

        Microsoft::WRL::ComPtr<IWICBitmapDecoder> WicBitmapDecoder;
        Sehnsucht::ThrowIfFailed(
            m_wicFactory->CreateDecoderFromStream(
                wicStream.Get(),
                nullptr,
                WICDecodeMetadataCacheOnDemand,
                &WicBitmapDecoder
                )
            );

        Microsoft::WRL::ComPtr<IWICBitmapFrameDecode> wicBitmapFrame;
        Sehnsucht::ThrowIfFailed(
            WicBitmapDecoder->GetFrame(0, &wicBitmapFrame)
            );

        Microsoft::WRL::ComPtr<IWICFormatConverter> wicFormatConverter;
        Sehnsucht::ThrowIfFailed(
            m_wicFactory->CreateFormatConverter(&wicFormatConverter)
            );

        Sehnsucht::ThrowIfFailed(
            wicFormatConverter->Initialize(
                wicBitmapFrame.Get(), GUID_WICPixelFormat32bppPBGRA,
                WICBitmapDitherTypeNone, nullptr, 0.0,
                WICBitmapPaletteTypeCustom
                )
            );

        UInt32 width;
        UInt32 height;
        Sehnsucht::ThrowIfFailed(
            wicBitmapFrame->GetSize(&width, &height)
            );

        std::unique_ptr<byte[]> bitmapPixelsUnqPtr( alloc_new byte[width * height * 4] );
        Sehnsucht::ThrowIfFailed(
            wicFormatConverter->CopyPixels(
                nullptr,
                width * 4,
                width * height * 4,
                bitmapPixelsUnqPtr.get()
                )
            );

        D3D11_SUBRESOURCE_DATA initialData;
        ZeroMemory(&initialData, sizeof(D3D11_SUBRESOURCE_DATA));
        initialData.pSysMem = bitmapPixelsUnqPtr.get();
        initialData.SysMemPitch = width * 4;
        initialData.SysMemSlicePitch = 0;

        CD3D11_TEXTURE2D_DESC textureDesc(
            DXGI_FORMAT_B8G8R8A8_UNORM,
            width, height,
            1, 1 // 1 element in the array / 1 mip level
            );

        Sehnsucht::ThrowIfFailed(
            m_d3dDevice->CreateTexture2D(
                &textureDesc,
                &initialData,
                &tempTexture
                )
            );

        if ( ppView != nullptr )
        {
            CD3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc(
                tempTexture.Get(), D3D11_SRV_DIMENSION_TEXTURE2D
                );

            Sehnsucht::ThrowIfFailed(
                m_d3dDevice->CreateShaderResourceView(
                    tempTexture.Get(),
                    &shaderResourceViewDesc,
                    &tempView
                    )
                );
        }

#endif
	}

	if ( GetStringLength(wszName) ) 
	{
		SetDebugName( 
			(tempTexture.Get()), // Shader address to set the debug name to
			GetStringData(wszName) // Debug name
			);
	}
	else
	{
		char szName[] = "BasicLoaderTexture";
		SetDebugName( 
			(tempTexture.Get()), // Shader address to set the debug name to
			szName // Debug name
			);
	}

    if ( ppTexture ) (*ppTexture) = tempTexture.Detach();
    if ( ppView ) (*ppView) = tempView.Detach();

	tempTexture = nullptr;
	tempView = nullptr;

	/*tempView.ReleaseAndGetAddressOf();
	tempTexture.ReleaseAndGetAddressOf();*/
}

Void BasicLoader::CreateInputLayout(
    _In_reads_bytes_(uBytecodeSize) BytePtr pBytecode,
    _In_ UInt32 uBytecodeSize,
    _In_reads_opt_(uLayoutDescNumElements) D3D11_INPUT_ELEMENT_DESC* pLayoutDescElements,
    _In_ UInt32 uLayoutDescNumElements,
    _Out_ InputLayoutAddress ppLayout,
    _In_opt_ BasicLoader::StringArg wszName
    )
{
    if ( !pLayoutDescElements )
    {
        // If no input layout is specified, use the BasicVertex layout.
        const D3D11_INPUT_ELEMENT_DESC aBasicVertexLayoutDesc[] =
        {
            { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,  D3D11_INPUT_PER_VERTEX_DATA, 0 },
            { "NORMAL",   0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 },
            { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT,    0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
        };

        Sehnsucht::ThrowIfFailed(
            m_d3dDevice->CreateInputLayout(
                aBasicVertexLayoutDesc,
                ARRAYSIZE(aBasicVertexLayoutDesc),
                pBytecode,
                uBytecodeSize,
                ppLayout
                )
            );
    }
    else
    {
        Sehnsucht::ThrowIfFailed(
            m_d3dDevice->CreateInputLayout(
                pLayoutDescElements,
                uLayoutDescNumElements,
                pBytecode,
                uBytecodeSize,
                ppLayout
                )
            );
    }

	if ( GetStringLength(wszName) ) 
	{
		SetDebugName( (*ppLayout), GetStringData(wszName) );
	}
	else
	{
		char szName[] = "BasicLoaderInputLayout";
		SetDebugName( (*ppLayout), szName );
	}
}



BasicLoader::Action BasicLoader::LoadTexture(
	_In_ BasicLoader::StringArg wszFileName, 
    _Outptr_opt_result_nullonfailure_ Texture2Address ppTexture,
    _Outptr_opt_result_nullonfailure_ ShaderResourceViewAddress ppView
	)
{
	auto textureBuffer = m_Reader->ReadData(wszFileName);
	_Auto_const decode = !IsDDSExtension(GetExtension(wszFileName));

	return CreateTexture(
		decode,
		textureBuffer->Data,
		textureBuffer->Length,
		ppTexture,
		ppView,
		wszFileName
		);
}

BasicLoader::AsyncAction BasicLoader::LoadTextureAsync(
	_In_ BasicLoader::StringArg wszFileName, 
    _Outptr_opt_result_nullonfailure_ Texture2Address ppTexture,
    _Outptr_opt_result_nullonfailure_ ShaderResourceViewAddress ppView
	)
{
	return m_Reader->ReadDataAsync(wszFileName).then( [=]( _In_ Reader::ByteArrayConstPtr TexBuffer)
	{
		return CreateTexture(
			IsDDSExtension(wszFileName),
			TexBuffer->Data,
			TexBuffer->Length,
			ppTexture,
			ppView,
			wszFileName
			);
	});
}

// !!!
// The most complex cases here : Vertex shader and Geometry shader

// Vertex shader

BasicLoader::Action BasicLoader::LoadShader(
    _In_ BasicLoader::StringArg wszFileName,
    _In_reads_opt_(layoutDescNumElements) D3D11_INPUT_ELEMENT_DESC pLayoutDescElements[],
    _In_ UInt32 uLayoutDescNumElements,
    _Outptr_result_nullonfailure_ VertexShaderAddress ppShader,
    _Outptr_opt_result_nullonfailure_ InputLayoutAddress ppLayout
    )
{
	auto Bytecode = m_Reader->ReadData(wszFileName);
    Sehnsucht::ThrowIfFailed(
        m_d3dDevice->CreateVertexShader(
            Bytecode->Data,
            Bytecode->Length,
            nullptr,
            ppShader
            )
        );

    SetDebugName( 
		(*ppShader), // Shader address to set the debug name to
		GetStringData(wszFileName) // Debug name
		);

    if ( ppLayout != nullptr)
    {
        CreateInputLayout(
            Bytecode->Data,
            Bytecode->Length,
            pLayoutDescElements,
            uLayoutDescNumElements,
            ppLayout,
			wszFileName
            );
    }
}

// Vertex shader

BasicLoader::AsyncAction BasicLoader::LoadShaderAsync(
    _In_ BasicLoader::StringArg wszFileName,
    _In_reads_opt_(layoutDescNumElements) D3D11_INPUT_ELEMENT_DESC pLayoutDescElements[],
    _In_ UInt32 uLayoutDescNumElements,
    _Outptr_result_nullonfailure_ VertexShaderAddress ppShader,
    _Outptr_opt_result_nullonfailure_ InputLayoutAddress ppLayout
    )
{
	typedef std::vector<D3D11_INPUT_ELEMENT_DESC> LayoutDescElements;
	typedef std::shared_ptr<LayoutDescElements> SharedLayoutDescElements;
	typedef std::vector<String8> SemanticNames;
	typedef std::shared_ptr<SemanticNames> SharedSemanticNames;

	// !!!
    // This method assumes that the lifetime of input arguments may be shorter than the duration of this task.  
	// In order to ensure accurate results, a copy of all arguments passed by pointer must be made.  The method 
	// then ensures that the lifetime of the copied data exceeds that of the task.

    // Create copies of the layoutDesc array as well as the SemanticName strings, both of which are pointers to 
	// data whose lifetimes may be shorter than that of this method's task.

	SharedLayoutDescElements LayoutDescElementsCopy;
	SharedSemanticNames LayoutDescElementsNamesCopy;

	if ( !pLayoutDescElements )
    {
        LayoutDescElementsCopy.reset(
            alloc_new LayoutDescElements(
                pLayoutDescElements, // First
                pLayoutDescElements + uLayoutDescNumElements // Last
                )
            );

        LayoutDescElementsNamesCopy.reset(
            alloc_new SemanticNames(uLayoutDescNumElements)
            );

        for ( auto i = 0u; i < uLayoutDescNumElements; i++ ) // Loop through all elements
        {
            LayoutDescElementsNamesCopy->at(i).assign( // and copy data 
				pLayoutDescElements[i].SemanticName // Get semantic name
				);
        }
    }

    return m_Reader->ReadDataAsync(wszFileName).then( [=]( _In_ Reader::ByteArrayConstPtr Bytecode )
    {
        Sehnsucht::ThrowIfFailed(
            m_d3dDevice->CreateVertexShader(
                Bytecode->Data,
                Bytecode->Length,
                nullptr,
                ppShader
                )
            );

        SetDebugName( 
			(*ppShader), // Shader address to set the debug name to
			GetStringData(wszFileName) // Debug name
			);

        if ( !ppLayout )
        {
            if ( !pLayoutDescElements )
            {
				// !!!
                // Reassign the SemanticName elements of the layoutDesc array copy to point to the corresponding copied strings. 
				// Performing the assignment inside the lambda body ensures that the lambda will take a reference to the shared_ptr
                // that holds the data. This will guarantee that the data is still valid when CreateInputLayout is called.

                for ( auto i = 0u; i < uLayoutDescNumElements; i++)
                    LayoutDescElementsCopy->at(i).SemanticName = 
						LayoutDescElementsNamesCopy->at(i).c_str();
            }

            CreateInputLayout(
                Bytecode->Data, // buffered data
                Bytecode->Length, // buffer length 
                pLayoutDescElements != nullptr 
					? LayoutDescElementsCopy->data() 
					: nullptr,
                uLayoutDescNumElements,
                ppLayout,
				wszFileName
                );
        }
    });
}

// Geometry shader

BasicLoader::Action BasicLoader::LoadShader(
    _In_ BasicLoader::StringArg wszFileName,
    _In_reads_opt_(uNumEntries) const D3D11_SO_DECLARATION_ENTRY streamOutDeclaration[],
    _In_ UInt32 uNumEntries,
    _In_reads_opt_(uNumStrides) const UInt32 pBufferStrides[],
    _In_ UInt32 uNumStrides,
    _In_ UInt32 uRasterizedStream,
    _Outptr_result_nullonfailure_ GeometryShaderAddress ppShader
    )
{
    auto Bytecode = m_Reader->ReadData(wszFileName);

    Sehnsucht::ThrowIfFailed(
        m_d3dDevice->CreateGeometryShaderWithStreamOutput(
            Bytecode->Data,
            Bytecode->Length,
            streamOutDeclaration,
            uNumEntries,
            pBufferStrides,
            uNumStrides,
            uRasterizedStream,
            nullptr,
            ppShader
            )
        );

   SetDebugName( 
		(*ppShader), // Shader address to set the debug name to
		GetStringData(wszFileName) // Debug name
		);
}

BasicLoader::AsyncAction BasicLoader::LoadShaderAsync(
    _In_ BasicLoader::StringArg wszFileName,
    _In_reads_opt_(uNumEntries) const D3D11_SO_DECLARATION_ENTRY pStreamOutDeclaration[],
    _In_ UInt32 uNumEntries,
    _In_reads_opt_(uNumStrides) const UInt32 pBufferStrides[],
    _In_ UInt32 uNumStrides,
    _In_ UInt32 uRasterizedStream,
    _Outptr_result_nullonfailure_ GeometryShaderAddress ppShader
    )
{
	typedef std::vector<D3D11_SO_DECLARATION_ENTRY> StreamOutputDeclEntryElements;
	typedef std::shared_ptr<StreamOutputDeclEntryElements> SharedStreamOutputDeclEntryElements;
	typedef std::vector<String8> SemanticNames;
	typedef std::shared_ptr<SemanticNames> SharedSemanticNames;
	typedef std::vector<UInt32> BufferStrides;
	typedef std::shared_ptr<BufferStrides> SharedBufferStrides;

	// !!!
    // This method assumes that the lifetime of input arguments may be shorter than the duration of this task.  
	// In order to ensure accurate results, a copy of all arguments passed by pointer must be made.  The method 
	// then ensures that the lifetime of the copied data exceeds that of the task.

    // Create copies of the layoutDesc array as well as the SemanticName strings, both of which are pointers to 
	// data whose lifetimes may be shorter than that of this method's task.

	SharedStreamOutputDeclEntryElements StreamOutDeclarationCopy;
	SharedSemanticNames StreamOutDeclarationNamesCopy;

	if ( !pStreamOutDeclaration )
    {
        StreamOutDeclarationCopy.reset(
            alloc_new StreamOutputDeclEntryElements(
                pStreamOutDeclaration, // First
                pStreamOutDeclaration + uNumEntries // Last
                )
            );

        StreamOutDeclarationNamesCopy.reset(
            alloc_new SemanticNames(uNumEntries)
            );

        for ( auto i = 0u; i < uNumEntries; i++ ) // Loop through all elements
        {
            StreamOutDeclarationNamesCopy->at(i).assign( // and copy data 
				pStreamOutDeclaration[i].SemanticName // Get semantic name
				);
        }
    }
    
	// !!!
    // Create a copy of the buffer strides array, which is a pointer to data
    // whose lifetime may be shorter than that of this method's task.

    SharedBufferStrides BufferStridesCopy;
    if ( !pBufferStrides )
    {
        BufferStridesCopy.reset(
            alloc_new BufferStrides(
                pBufferStrides,
                pBufferStrides + uNumStrides
                )
            );
    }

    return m_Reader->ReadDataAsync(wszFileName).then( [=]( _In_ Reader::ByteArrayConstPtr Bytecode )
    {
        if ( !pStreamOutDeclaration )
        {
			// !!!
            // Reassign the SemanticName elements of the stream out declaration array copy to
            // point to the corresponding copied strings. Performing the assignment inside the
            // lambda body ensures that the lambda will take a reference to the shared_ptr
            // that holds the data.  This will guarantee that the data is still valid when
            // CreateGeometryShaderWithStreamOutput is called.

            for ( auto i = 0u; i < uNumEntries; i++ )
            {
                StreamOutDeclarationCopy->at(i).SemanticName = 
					StreamOutDeclarationNamesCopy->at(i).c_str();
            }
        }

        Sehnsucht::ThrowIfFailed(
            m_d3dDevice->CreateGeometryShaderWithStreamOutput(
                Bytecode->Data,
                Bytecode->Length,
                pStreamOutDeclaration != nullptr 
					? StreamOutDeclarationCopy->data() 
					: nullptr,
                uNumEntries,
                pBufferStrides != nullptr 
					? BufferStridesCopy->data() 
					: nullptr ,
                uNumStrides,
                uRasterizedStream,
                nullptr,
                ppShader
                )
            );

	   SetDebugName( 
			(*ppShader), // Shader address to set the debug name to
			GetStringData(wszFileName) // Debug name
			);
    });
}

// Hull shader

BasicLoader::Action BasicLoader::LoadShader(
    _In_ StringArg wszFileName,
    _Outptr_result_nullonfailure_ HullShaderAddress ppShader
    )
{
    auto Bytecode = m_Reader->ReadData(wszFileName);
    Sehnsucht::ThrowIfFailed(
        m_d3dDevice->CreateHullShader(
            Bytecode->Data,
            Bytecode->Length,
            nullptr,
            ppShader
            )
        );

   SetDebugName( 
		(*ppShader), // Shader address to set the debug name to
		GetStringData(wszFileName) // Debug name
		);
}

BasicLoader::AsyncAction BasicLoader::LoadShaderAsync(
    _In_ StringArg wszFileName,
    _Outptr_result_nullonfailure_ HullShaderAddress ppShader
    )
{
   return m_Reader->ReadDataAsync(wszFileName).then( [=]( _In_ Reader::ByteArrayConstPtr Bytecode )
   {
	    Sehnsucht::ThrowIfFailed(
			m_d3dDevice->CreateHullShader(
				Bytecode->Data,
				Bytecode->Length,
				nullptr,
				ppShader
				)
			);

		SetDebugName( 
			(*ppShader), // Shader address to set the debug name to
			GetStringData(wszFileName) // Debug name
			);
   });
}

// Domain shader

BasicLoader::Action BasicLoader::LoadShader(
    _In_ StringArg wszFileName,
    _Outptr_result_nullonfailure_ DomainShaderAddress ppShader
    )
{
    auto Bytecode = m_Reader->ReadData(wszFileName);
    Sehnsucht::ThrowIfFailed(
        m_d3dDevice->CreateDomainShader(
            Bytecode->Data,
            Bytecode->Length,
            nullptr,
            ppShader
            )
        );

	SetDebugName( 
		(*ppShader), // Shader address to set the debug name to
		GetStringData(wszFileName) // Debug name
		);
}

BasicLoader::AsyncAction BasicLoader::LoadShaderAsync(
    _In_ StringArg wszFileName,
    _Outptr_result_nullonfailure_ DomainShaderAddress ppShader
    )
{
   return m_Reader->ReadDataAsync(wszFileName).then( [=]( _In_ Reader::ByteArrayConstPtr Bytecode )
   {
	    Sehnsucht::ThrowIfFailed(
			m_d3dDevice->CreateDomainShader(
				Bytecode->Data,
				Bytecode->Length,
				nullptr,
				ppShader
				)
			);

		SetDebugName( 
			(*ppShader), // Shader address to set the debug name to
			GetStringData(wszFileName) // Debug name
			);
   });
}

// Geometry shader

BasicLoader::Action BasicLoader::LoadShader(
    _In_ StringArg wszFileName,
    _Outptr_result_nullonfailure_ GeometryShaderAddress ppShader
    )
{
    auto Bytecode = m_Reader->ReadData(wszFileName);
    Sehnsucht::ThrowIfFailed(
        m_d3dDevice->CreateGeometryShader(
            Bytecode->Data,
            Bytecode->Length,
            nullptr,
            ppShader
            )
        );

	SetDebugName( 
		(*ppShader), // Shader address to set the debug name to
		GetStringData(wszFileName) // Debug name
		);
}

BasicLoader::AsyncAction BasicLoader::LoadShaderAsync(
    _In_ StringArg wszFileName,
    _Outptr_result_nullonfailure_ GeometryShaderAddress ppShader
    )
{
   return m_Reader->ReadDataAsync(wszFileName).then( [=]( _In_ Reader::ByteArrayConstPtr Bytecode )
   {
	    Sehnsucht::ThrowIfFailed(
			m_d3dDevice->CreateGeometryShader(
				Bytecode->Data,
				Bytecode->Length,
				nullptr,
				ppShader
				)
			);

		SetDebugName( 
			(*ppShader), // Shader address to set the debug name to
			GetStringData(wszFileName) // Debug name
			);
   });
}

// Pixel shader

BasicLoader::Action BasicLoader::LoadShader(
    _In_ StringArg wszFileName,
    _Outptr_result_nullonfailure_ PixelShaderAddress ppShader
    )
{
    auto Bytecode = m_Reader->ReadData(wszFileName);
    Sehnsucht::ThrowIfFailed(
        m_d3dDevice->CreatePixelShader(
            Bytecode->Data,
            Bytecode->Length,
            nullptr,
            ppShader
            )
        );

	SetDebugName( 
		(*ppShader), // Shader address to set the debug name to
		GetStringData(wszFileName) // Debug name
		);
}

BasicLoader::AsyncAction BasicLoader::LoadShaderAsync(
    _In_ StringArg wszFileName,
    _Outptr_result_nullonfailure_ PixelShaderAddress ppShader
    )
{
   return m_Reader->ReadDataAsync(wszFileName).then( [=]( _In_ Reader::ByteArrayConstPtr Bytecode )
   {
	    Sehnsucht::ThrowIfFailed(
			m_d3dDevice->CreatePixelShader(
				Bytecode->Data,
				Bytecode->Length,
				nullptr,
				ppShader
				)
			);

		SetDebugName( 
			(*ppShader), // Shader address to set the debug name to
			GetStringData(wszFileName) // Debug name
			);
   });
}

// Compute shader

BasicLoader::Action BasicLoader::LoadShader(
    _In_ StringArg wszFileName,
    _Outptr_result_nullonfailure_ ComputeShaderAddress ppShader
    )
{
    auto Bytecode = m_Reader->ReadData(wszFileName);
    Sehnsucht::ThrowIfFailed(
        m_d3dDevice->CreateComputeShader(
            Bytecode->Data,
            Bytecode->Length,
            nullptr,
            ppShader
            )
        );

	SetDebugName( 
		(*ppShader), // Shader address to set the debug name to
		GetStringData(wszFileName) // Debug name
		);
}

BasicLoader::AsyncAction BasicLoader::LoadShaderAsync(
    _In_ StringArg wszFileName,
    _Outptr_result_nullonfailure_ ComputeShaderAddress ppShader
    )
{
   return m_Reader->ReadDataAsync(wszFileName).then( [=]( _In_ Reader::ByteArrayConstPtr Bytecode )
   {
	    Sehnsucht::ThrowIfFailed(
			m_d3dDevice->CreateComputeShader(
				Bytecode->Data,
				Bytecode->Length,
				nullptr,
				ppShader
				)
			);

		SetDebugName( 
			(*ppShader), // Shader address to set the debug name to
			GetStringData(wszFileName) // Debug name
			);
   });
}
